/*
  这里封装了一些树的操作方法
 */
import utils from '@/common/utils.js'

export default {
  /**
   * 从树状数据结构中获取所有的叶节点
   * 该方法主要是用于el-tree相关组件在父子关联情况下的回显.如果需要默认勾选的节点中包含非页节点,会导致父节点下的所有节点都会被勾选.,这时就需要通过仅勾选叶节点来实现...
   * @param {[]} tree 数据源
   * @param {string} children 子节点属性名
   * @param {boolean} keyOnly 是否需要返回整个叶节点,还是仅需要返回ID
   * @param {string} nodeKey 需要返回的属性名 仅idOnly=true 有效 如果不需要返回整个节点,需要返回的属性名默认为id
   */
  getLeaves(tree, {keyOnly = true, nodeKey = 'id', children = 'children'} = {}) {
    if (Array.isArray(tree)) {
      let leaves = []
      let queue = []
      queue.push(...tree)
      while (queue[0]) {
        // 如果有子节点
        if (Array.isArray(queue[0][children]) && queue[0][children].length) {
          queue.push(...queue[0][children])
        } else {
          leaves.push(keyOnly ? queue[0][nodeKey] : queue[0])
        }
        // 队列出
        queue.shift()
      }
      return leaves
    } else {
      throw Error('not Array!')
    }
  },
  /**
   * 遍历树,可以添加参数
   * @param {array} tree - 数据源
   * @param {function} action - 操作
   * @param {string} children - 子节点的属性名
   * @return {array} 树
   */
  iterator(tree, action, {children = 'children'} = {}) {
    let queue = []
    queue.push(...tree)
    while (queue.length) {
      let node = queue[0]
      if (Array.isArray(node[children]) && node[children].length) {
        queue.push(...node[children])
      }
      action(node, tree)
      queue.shift()
    }
    return tree
  },
  /**
   * 根据条件查找树
   * @param {array} tree 数据源
   * @param {function} filter 筛选的方法
   * @return {array} res - 筛选结果
   */
  findAll(tree, filter) {
    let res = []
    this.iterator(tree, (node) => {
      if (filter(node, tree)) {
        res.push(node)
      }
    })
    return res
  },
  /**
   * 子节点冗余父节点, 一般用于展示子结构展示父名称
   * @param {array} tree - 数据源
   * @param {string} children - 子节点的属性名
   * @param {string} source - 父的属性名
   * @param {string} target - 要转换成的子的属性名
   */
  linkParent(tree, source = 'name', target = 'parentName', {children = 'children'} = {}) {
    this.iterator(tree, (node) => {
      if (Array.isArray(node[children]) && node[children].length) {
        node[children].forEach(child => {
          child[target] = node[source]
        })
      }
    }, {children})
  },
  /**
   * 树结构转成数组
   * @param {array} tree - 数据源
   * @param {string} [children = 'children'] 子节点的属性名
   * @return {array} res - 转换后的数组
   */
  toList(tree, {children = 'children'} = {}) {
    let res = []
    this.iterator(tree, (node) => {
      res.push(node)
    }, {children})
    return res
  },
  /**
   * 获取某树节点的路径数组(从父到子的顺序) todo
   * @param {array} tree - 数据源
   * @param {string} [children = 'children'] 子节点的属性名
   * @param {string} key
   * @param {string} [nodeKey = 'id'] 需要返回的节点的属性
   * @return {object} res.keys
   *
   */
  getPath(tree, key, {children = 'children', nodeKey = 'id', reverse = false} = {}) {

  },
  /**
   * 添加一个头部节点
   * @param {Object} tree - 数据源
   * @param {Object} header - 头部的内容
   * @param {string} [children='children'] - 子节点的属性名
   */
  addHeader(tree, header = {id: '0', name: '头部'}, {children = 'children'}) {
    if (Array.isArray(tree)) {
      return utils.addHeader(tree, {isTree: true, children: children, content: header})
    } else {
      throw new Error('数据格式有误!')
    }
  },
  /**
   * 树节点的过滤
   * @param tree
   * @param action
   * @returns {any}
   */
  filter(tree, action, children = 'children') {
    let newTree = JSON.parse(JSON.stringify(tree))
    let res = {}
    res[children] = newTree
    const iterator = (_node) => {
      if (Array.isArray(_node[children])) {
        for (let i = 0; i < _node[children].length; i++) {
          if (!action(_node[children][i])) {
            _node[children].splice(i, 1)
            i--
          } else {
            iterator(_node[children][i])
          }
        }
        if(!_node[children].length){
          delete(_node[children])
        }
      }
    }
    iterator(res)
    return res.children || []
  }
}

